//`include "sram.v"

module apbSlaveSramInterface(PCLK,
                             PRESETn,
                             PADDR,
                             PPROT,
                             PSEL,
                             PENABLE,
                             PSTRB,
                             PWRITE,
                             PWDATA,
                             PREADY,
                             PRDATA,
                             PSLVERR,
                             mem_addr,   //SRAM memory signals
                             mem_write,
                             mem_strb,
                             mem_wdata,
                             mem_rdata,
			     			 mem_rvalid);
  
  //parameter declarations
  parameter DATA_WIDTH	 = 32;
  parameter MEMORY_WIDTH = 1024;
  parameter ADDR_WIDTH   = 10; //log2(MEMORY_WIDTH);
  parameter STRB_WIDTH	 = 4; //log2(DATA_WIDTH-8);
  
  //APB slave signals declarations
  input 					PCLK;
  input 					PRESETn;
  input  [ADDR_WIDTH-1:0] 	PADDR;
  input                     PPROT;
  input						PSEL;
  input                     PENABLE;
  input                     PWRITE;
  input  [DATA_WIDTH-1:0]	PWDATA;
  input  [STRB_WIDTH-1:0]	PSTRB;
  
  output reg                	PREADY;
  output reg  [DATA_WIDTH-1:0]	PRDATA;
  output reg                	PSLVERR;
  
  //SRAM memory signals declarations
  output reg [ADDR_WIDTH-1:0]	mem_addr;
  output reg                    mem_write;
  output reg [STRB_WIDTH-1:0]	mem_strb;
  output reg [DATA_WIDTH-1:0]	mem_wdata;
  input							mem_rvalid;
  input      [DATA_WIDTH-1:0]	mem_rdata;
  
  //APB slave fsm declarations
  reg [1:0] apbSlaveState, apbSlaveNState, d1, d2;
  //parameter idle   = 0;
  parameter SETUP  	 	= 0;
  parameter WRITE_ACCESS = 1;
  parameter READ_ACCESS  = 2;
  integer n;
  always @(posedge PCLK or negedge PRESETn) begin
    if(PRESETn == 0)begin
      apbSlaveState <= SETUP;
      apbSlaveNState <= SETUP;
      PRDATA <= 0;
      PSLVERR <= 0;
      PREADY <= 0;
      mem_write <= 0;
      d1 <= SETUP;
      d2 <= SETUP;
      //mem_strb <= 0;
    end
    else begin
      delayedState;
    end
  end
  always @(apbSlaveState or mem_rdata or mem_rvalid or PSEL or PENABLE) begin
      if(PRESETn) begin
      case(apbSlaveState)
        SETUP:begin
          PRDATA <= {DATA_WIDTH{1'b0}};
          //mem_addr <= PADDR;
          if(PSEL && !PENABLE) begin
            if(PWRITE == 1) begin
              apbSlaveNState <= WRITE_ACCESS;
              n = $urandom % 3;
            end
            else begin
              apbSlaveNState <= READ_ACCESS;
              n = $urandom % 3;
            end
            PREADY <= 1'b0;
          end          
        end
        
        WRITE_ACCESS:begin
          if(PSEL && PENABLE && PWRITE) begin
            mem_addr <= PADDR;
            mem_write <= 1'b1;
            mem_strb <= 4'b1111;
            mem_wdata <= PWDATA;
          end
          if(mem_rvalid == 0) begin
            PSLVERR <= 1;
          end
          else begin
          	PSLVERR <= 0;
          end
          apbSlaveNState <= SETUP;
          n = 0;
          PREADY <= 1'b1;          
        end
        
        READ_ACCESS:begin
          if(PSEL && PENABLE && !PWRITE) begin
            mem_addr <= PADDR;
            mem_write <= 1'b0;
            mem_strb <= 4'b0000;
            PRDATA <= mem_rdata;
          end
          if(mem_rvalid == 0) begin
            PSLVERR <= 1;
          end
          else begin
          	PSLVERR <= 0;
          end
          apbSlaveNState <= SETUP;
          n = 0;
          PREADY <= 1'b1;  
        end
        
        default: apbSlaveNState <= SETUP;
      endcase
    end
  end
  /*
  function integer log2;
	input integer value;
	begin
		value = value-1;
		for(log2 = 0; value>0; log2=log2+1)
			value = value>>1;
	end
  endfunction*/
  task delayedState;
    begin
  	  case(n)
        0:begin
          apbSlaveState <= apbSlaveNState;
        end
 		1:begin
          	apbSlaveState <= d1;
      	  	d1 <= apbSlaveNState;
        end
        2:begin 
          	apbSlaveState <= d1;
      	  	d1 <= d2;
      	  	d2 <= apbSlaveNState;
        end
      endcase
    end
  endtask
endmodule
